Wczytywanie danych

Swoją pracę rozpoczęłam od przygotowania bazy danych. W porozumieniu z firmą X weszłam w posiadanie danych sprzedażowych za rok 2017 oraz 2018. Firma zajmuje się detaliczną oraz hurtową dystrybucją odzieży i tekstyliów w Polsce. Dostarczone dane zostały odpowiednio okrojone poprzez usunięcie rekordów z topowymi klientami. Zobowiązałam się również, iż w związku ustawą RODO, usunę wszystkie dane osobowe, pozwalające na identyfikację klientów firmy. Dlatego usunęłam kolumny przedstawiające szczegółowe dane adresowe i kontaktowe. Nazwy kontrahentów podmieniłam na nazwiska olimpijczyków z zewnętrznej bazy danych. Dostarczone baza została wyeksportowana z systemu ERP firmy X i zapisana w postaci plik excel.

Przed wczytaniem danych do RStudio przyjrzałam im się w excelu i zaplanowałam jakie wizualizacje będę tworzyła. Ponieważ dane były zaprezentowane w różnych arkuszach, wybrałam interesujące mnie elementy i za pomocą formuły v-lookup zestawiłam w jednej tabeli. Aby wykonać niektóre z wybranych przez siebie wykresów, musiałam wyszukać w Internecie dodatkowe dane i dodać je do tabeli głównej, m.in. koordynaty geograficzne polskich miast.

Następnie zaimportowałam dane do RStudio. Tam stworzyłam tabele, do której dodałam trzy interesujące mnie kolumny: zysk w euro, całkowita sprzedaż w euro oraz marża. Ponieważ firma, sprzedane towary zakupuje w walucie euro, a sprzedaje w euro jak i w polskich złotych, postanowiłam do celów analitycznych wybrać walutę euro jako główną.

sample1 <- read_delim("sample1.csv", 
                      delim = ";", 
                      escape_double = FALSE, 
                      col_types = cols(`Operation Date` = col_date(format = "%Y%m%d"), 
                                       Year = col_factor(levels = c("2017", "2018")),  
                                       lat = col_number(),
                                       long = col_number()),
                     trim_ws = TRUE)

#wyfiltrowac korekty document typy i fracht item type


dane <- sample1 %>% filter(`Document Type` != "Credit Note",
                `Item Type` != "Fracht") %>%
  mutate(total_sales_in_euro = Quantity*`Item Price`/`Euro Rate`, 
                           profit_in_euro = total_sales_in_euro - `MEK Purchase Value`,
                           margin = profit_in_euro/total_sales_in_euro)

Co przedstawiają moje dane?

Moje dane zawierają szczegóły transakcji sprzedażowych firmy X. W kolumnie „Document Type” zawarta jest informacja czy dana operacja była sprzedażą, czy korektą sprzedaży. Będę się zajmowała jedynie sprzedażą, dlatego wybieram typ: Invoice. W kolejnych kolumnach mamy informacje o kontrahencie firmy oraz podstawowe informacje adresowe (kod pocztowy, miejscowość oraz współrzędne geograficzne). W dalszej części tabeli możemy dużo dowiedzieć się o sprzedanym produkcie, min. o jego rodzaju, kolorze, rozmiarze cenie sprzedażowej jak i zakupowej, walucie operacji i sprzedanym wolumenie.

W przedstawionym poniżej projekcie, będę analizowała sprzedaż firmy X, jej strukturę, wysokość, rozłożenie.

str(dane)
## spec_tbl_df [532,616 x 33] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ Document Type         : chr [1:532616] "Invoice" "Invoice" "Invoice" "Invoice" ...
##  $ Client Number         : num [1:532616] 5053313 5053313 5053313 5053313 5053313 ...
##  $ Client Name           : chr [1:532616] "David Kostelecky" "David Kostelecky" "David Kostelecky" "David Kostelecky" ...
##  $ Address 2             : chr [1:532616] "01-461" "01-461" "01-461" "01-461" ...
##  $ Address 3             : chr [1:532616] "Warszawa" "Warszawa" "Warszawa" "Warszawa" ...
##  $ lat                   : num [1:532616] 52.2 52.2 52.2 52.2 52.2 ...
##  $ long                  : num [1:532616] 21 21 21 21 21 ...
##  $ Payment Method        : chr [1:532616] "7 days" "7 days" "7 days" "7 days" ...
##  $ Document number       : num [1:532616] 2.02e+13 2.02e+13 2.02e+13 2.02e+13 2.02e+13 ...
##  $ Operation Date        : Date[1:532616], format: "2017-01-02" "2017-01-02" ...
##  $ Year                  : Factor w/ 2 levels "2017","2018": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Month                 : chr [1:532616] "January" "January" "January" "January" ...
##  $ Item Code             : chr [1:532616] "162851283" "162851284" "162851285" "162851286" ...
##  $ Item Color            : chr [1:532616] "128" "128" "128" "128" ...
##  $ Group Color           : chr [1:532616] "Grey" "Grey" "Grey" "Grey" ...
##  $ Item Size             : num [1:532616] 3 4 5 6 7 4 6 4 5 3 ...
##  $ Supplier              : chr [1:532616] "85" "85" "85" "85" ...
##  $ Style                 : chr [1:532616] "16285" "16285" "16285" "16285" ...
##  $ Item Description      : chr [1:532616] "Jack Asphalt S" "Jack Asphalt M" "Jack Asphalt L" "Jack Asphalt XL" ...
##  $ Quantity              : num [1:532616] 0 0 0 0 0 0 0 0 0 0 ...
##  $ Item Price            : num [1:532616] 22.4 22.4 22.4 22.4 22.4 ...
##  $ Currency              : chr [1:532616] "PLN" "PLN" "PLN" "PLN" ...
##  $ Euro Rate             : num [1:532616] 4.37 4.37 4.37 4.37 4.37 ...
##  $ Item Value in Euro    : num [1:532616] 0 0 0 0 0 0 0 0 0 0 ...
##  $ Item Type             : chr [1:532616] "Product" "Product" "Product" "Product" ...
##  $ Delivery Method       : chr [1:532616] "UPS" "UPS" "UPS" "UPS" ...
##  $ Indicator             : num [1:532616] 1.06 1.06 1.06 1.06 1.06 1.06 1.07 1.07 1.07 1.07 ...
##  $ MEK Item Price        : num [1:532616] 4.27 4.27 4.27 4.27 4.27 ...
##  $ MEK Purchase Value    : num [1:532616] 0 0 0 0 0 0 0 0 0 0 ...
##  $ Purchase Value Curency: chr [1:532616] "EUR" "EUR" "EUR" "EUR" ...
##  $ total_sales_in_euro   : num [1:532616] 0 0 0 0 0 0 0 0 0 0 ...
##  $ profit_in_euro        : num [1:532616] 0 0 0 0 0 0 0 0 0 0 ...
##  $ margin                : num [1:532616] NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   `Document Type` = col_character(),
##   ..   `Client Number` = col_double(),
##   ..   `Client Name` = col_character(),
##   ..   `Address 2` = col_character(),
##   ..   `Address 3` = col_character(),
##   ..   lat = col_number(),
##   ..   long = col_number(),
##   ..   `Payment Method` = col_character(),
##   ..   `Document number` = col_double(),
##   ..   `Operation Date` = col_date(format = "%Y%m%d"),
##   ..   Year = col_factor(levels = c("2017", "2018"), ordered = FALSE, include_na = FALSE),
##   ..   Month = col_character(),
##   ..   `Item Code` = col_character(),
##   ..   `Item Color` = col_character(),
##   ..   `Group Color` = col_character(),
##   ..   `Item Size` = col_double(),
##   ..   Supplier = col_character(),
##   ..   Style = col_character(),
##   ..   `Item Description` = col_character(),
##   ..   Quantity = col_double(),
##   ..   `Item Price` = col_double(),
##   ..   Currency = col_character(),
##   ..   `Euro Rate` = col_double(),
##   ..   `Item Value in Euro` = col_double(),
##   ..   `Item Type` = col_character(),
##   ..   `Delivery Method` = col_character(),
##   ..   Indicator = col_double(),
##   ..   `MEK Item Price` = col_double(),
##   ..   `MEK Purchase Value` = col_double(),
##   ..   `Purchase Value Curency` = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>

Wykres nr 1

Na pierwszym wykresie, przedstawiam dzienną wartość sprzedaży w analizowany okresie. Na stronie data-to-viz znalazłam odpowiedni do tego wykres. Funkcja slidera pozwala na zbliżenie i szczegółowe przejrzenie danych, ale moża również spojrzeć z perspektywy całego okresu.

sprzedaz_data <- dane %>% select(`Operation Date`, total_sales_in_euro)

don <- xts(x = sprzedaz_data$total_sales_in_euro, 
           order.by = sprzedaz_data$`Operation Date`)

dygraph(don, 
        main = "Dzienna wartość sprzedaży 2017-2018 ",
        xlab = "data",
        ylab = "wartość sprzedaży") %>%
  dyOptions(labelsUTC = TRUE, 
            fillGraph=TRUE, 
            fillAlpha=0.4, 
            drawGrid = FALSE, 
            colors="lightblu1") %>%
  dyRangeSelector() %>%
  dyCrosshair(direction = "vertical") %>%
  dyHighlight(highlightCircleSize = 5, 
              highlightSeriesBackgroundAlpha = 0.2, 
              hideOnMouseOut = FALSE)  %>%
  dyRoller(rollPeriod = 1)

Wnioski

Sprzedaż dzienna w analizowanej firmie jest nieregularna i cechuje ją wysoka zmienność. Może to wynikać z faktu, iż firma zajmuje się sprzedażą detaliczną jak i hurtową. Transakcje hurtowe powodują, iż dzienne wartości mogą istotnie różnić się od siebie. Fakt ten nie pozwala na wyciągnięcie wniosków o sezonowości sprzedaży. Dlatego poprzez agregację danym przyjrzę się im na przestrzeni kwartałów.

Wykres nr 2

Chcąc sprawdzić, czy na sprzedaż firmy X ma wpływ sezonowość, a jeśli tak, to który kwartał charakteryzuje się sprzedażą największej ilości produktów, postanowiłam przedstawić dane na slopegrafie. Aby pogrupować dane na kwartały, używam zmiennej system i w zależności od kwartału przypisuję mu wybrane miesiące, a następnie grupuję po latach i nowo utworzonej zmiennej system.

miesiace <- dane %>%
  group_by(Year, Month) %>%
  summarize(sum = sum(Quantity),
            ) %>%
  mutate(system = ifelse(Month %in% c("January", "February", "March"), "Q1",
                         ifelse(Month %in% c("April", "May", "June"), "Q2",
                                ifelse(Month %in%c("July", "August", "September" ),"Q3","Q4")))) %>%
  group_by(Year, Quarter = system) %>%
  summarize(sum_of_quantity = sum(sum)) %>% 
  as.data.frame()
## `summarise()` has grouped output by 'Year'. You can override using the `.groups` argument.
## `summarise()` has grouped output by 'Year'. You can override using the `.groups` argument.
miesiace %>% newggslopegraph(Times = Quarter, 
                             Measurement = sum_of_quantity, 
                             Grouping = Year, 
                             Title = "Ilosc sprzedanych produktów wg kwartalow",
                             SubTitle = "",
                             Caption = NULL,
                             LineThickness = 1.5,
                             LineColor = "lightblue3",
                             DataTextSize = 2.75,
                             DataTextColor = "black")

Wnioski

Na przedstawionym slopgrafie widzimy, iż największy wolumen sprzedaży to kwartał drugi, zarówno w roku 2017 jak i w roku 2018. Drugi największy wolumen jest w kwartale trzecim, również w następujących po sobie latach. Można z tego wywnioskować, iż największa ilość sprzedawanych produktów przypada na okres letni, od kwietnia do września, ze szczytem sezonu przypadającym na Q2. Z przedstawionego wykresu, widać również wyraźnie, że firma się rozwija i w każdym kwartale roku 2018 zwiększyła swój wolumen sprzedaży w porównaniu z rokiem 2017.

Wykres 3

Najbardziej ruchliwym kwartałem okazał się kwartał 2 w roku 2018. Przeanalizujmy ilość, oraz sprzedaż wg kolorów w jednym z miesięcy w tym kwartale.

dane %>% filter(Year == 2017, Month == "April") %>%
  ggplot(aes(x=Quantity, 
             y=total_sales_in_euro,
             color = as.factor(`Item Size`))) +
    geom_point(alpha=0.5, size = 2) +
  geom_smooth(method=lm , color="darkgrey", se=FALSE) +
  theme_bw() +
  labs(title = "Sprzedaż a ilość z podziałem na rozmiary w kwietniu",
       colour = "rozmiar",
       x = "ilość",
       y = "wartość sprzedaży")
## `geom_smooth()` using formula 'y ~ x'

Wykres nr 4

Kolejnym obszarem, który przeanalizowałam to dane geograficzne, pozwalające sprawdzić w jakiej lokalizacji w Polsce znajdują się najwięksi klienci firmy. W bazie posiadałam wyłącznie nazwę miejscowości oraz jej kod pocztowy. Na tej podstawie dodałam kolumnę z szerokościami i długościami geograficznymi tych miast. Niestety nie udało mi się znaleźć koordynatów do wszystkich miejscowości z bazy, dlatego rekordy nieposiadające lokalizacji geograficznych nie zostały uwzględnione w badaniu. Jednakże brakujące dane dotyczą najmniejszych miejscowości, których brak w analizie w niewielkim stopniu wpływa na wynik końcowy. Natomiast finalnie sprawiło to, że mój wykres przedstawia dane w przybliżeniu.

Poland <- map_data("world") %>% 
  filter(region=="Poland")

domapy <- dane %>% 
  select(`Address 3`,lat, long, total_sales_in_euro) %>%
  mutate(mytext = paste(
    "City: ",`Address 3`, "\n", 
    "Tot. sale: ", total_sales_in_euro, sep=""))

domapy[is.na(domapy)] <- 0

domapy <- domapy %>% filter(lat != 0)

ggplot() +
  geom_polygon(data = Poland, 
               aes(x=long, 
                   y = lat, 
                   group = group), 
               fill="gray47", 
               alpha=0.3) +
  geom_point(data = domapy, 
             aes(x = long, 
             y = lat, 
             size = total_sales_in_euro,
             text = mytext),
             colour = "darkblue") +
  theme_void() +
  labs("Wartoś sprzedaży dla poszczególnych miast",
       size = "Wartość sprzedaży")
## Warning: Ignoring unknown aesthetics: text

Wnioski

Analizowana firma, kieruje swoje produkty głównie do firm znajdujących się w dużych ośrodkach miejskich. Zdecydowanymi liderami są aglomeracja warszawska i krakowska, trójmiasto i konurbacja górnośląska. Warto tutaj podkreślić, iż mówimy o aglomeracjach a nie tylko głównych miastach regionu. Np. w Małopolsce, wysoką sprzedaż generuje nie tylko Kraków, ale również Wieliczka, Myślenice, Liszki, Skawina czy Niepołomice.

W nieco mniejszym stopniu, ale również z dużymi wartościami, w sprzedaży górują Wrocław, Poznań, Olsztyn. Za nimi są miasta do 200 000 mieszkańców jak Kielce czy Wałbrzych. Ogólny ogląd mapy sugeruje, iż sprzedaż odbywa się w ośrodkach miejskich i na terenach dobrze zurbanizowanych. Przedstawiona w analizie mapa, pozwala ocenić geograficzną strukturę sprzedaży i na jej podstawie przygotować dalszą strategię firmy.

Wykres nr 5

Kolejny analizowany aspekt to produktowa struktura sprzedaży. Ponieważ firma X ma bardzo szeroki asortyment, chciałam poznać jej najlepiej sprzedające się marki i produkty. Wyfiltrowałem 6 najlepiej sprzedających się marek. Przedstawię je na wykresie o nazwie dendrogram, który jest idealny do przedstawiania podziałów. Na początku zmieniam typ danych na listę węzłów. Następnie tworzę dendrogram, na którym za pomocą kolorów i wielkości pokazuję ilość sprzedanych produktów.

najlepsi_dostawcy <- dane %>%
  group_by(`Supplier`) %>%
  summarize(sum(Quantity)) %>%
  top_n(6)
## Selecting by sum(Quantity)
data1 <- dane %>% 
  filter(`Supplier`%in% najlepsi_dostawcy$`Supplier`) %>%
  group_by(`Supplier`, `Item Size`) %>%
  summarize(s1 = sum(Quantity), s2 = sum(profit_in_euro))
## `summarise()` has grouped output by 'Supplier'. You can override using the `.groups` argument.
data <- data.frame(
  level1="",
  level2=data1$Supplier,
  level3=data1$`Item Size`,
  level4=data1$s1)

# zmieniam ramkę danych na edge list!
edges_level1_2 <- data %>% select(level1, level2) %>% 
  unique %>% 
  rename(from=level1, to=level2)

edges_level2_3 <- data %>% 
  select(level2, level3) %>% 
  unique %>% 
  rename(from=level2, to=level3)

edge_list=rbind(edges_level1_2, edges_level2_3)

mygraph <- graph_from_data_frame(edge_list)

par(mar=c(0,0,0,0))
ggraph(mygraph, layout = 'dendrogram', circular = TRUE) + 
  geom_edge_diagonal(colour="grey") +
  geom_node_text(aes(x = x*1.09, y=y*1.09, label=c(unique(data$level1), 
                             unique(data$level2), 
                             data$level3)), hjust=1) +
  geom_node_point(aes(filter=leaf, 
                      size = c(0,najlepsi_dostawcy$`sum(Quantity)`, data$level4),
                      color = c(0,najlepsi_dostawcy$`sum(Quantity)`, data$level2))) +
  theme_void() +
  scale_fill_brewer(palette="Set2") +
  scale_color_brewer(palette = "Set2") +
  labs(size = "Ilość produktów",
       color = "supplier")
## Multiple parents. Unfolding graph

Wnioski

Przedstawiony wykres ujawnił nam strukturę głównych produktów firmy. Na czele sprzedawanych marek są marki 57, 42, 09, 01, 28 i 69. Marki 42, 01 i 09 mają bardzo podobną strukturę, dzielą się na dziesięć rozmiarów, a główna sprzedaż odbywa się na rozmiarach 4, 5, i 6. Natomiast marki 57, 28 i 69 wyglądają zupełnie inaczej. Tu sprzedaż odbywa się na jednym rozmiarze. Biorąc pod uwagę fakt, iż firma X zajmuje się dystrybucją tekstyliów, może to sugerować, że marki te są jednorozmiarowe – np.: czapki, torby, ręczniki.

Wykres nr 6

Aby jeszcze dokładniej przeanalizować strukturę produktów, postanowiłam przyjrzeć się zależności pomiędzy kolorem a rozmiarem sprzedawanych artykółów. Bardzo szeroką gamę kolorów, zorganizowałam w dużo mniejsze grupy, pozwalające na przejrzyste zaprezentowanie struktury. I tak wszystkie odcienie koloru niebieskiego zgrupowane są w kolorze „Blue”, wszystkie odcienie zieleni zgrupowane są w kolorze „Green”, wszystkie odcienie czerwieni w kolorze „Red” itd. Na tej podstawie stworzyłam zgrupowany wykres słupkowy.

dane1 <- dane %>% filter(`Item Color` != "Unknown")

kolor <- dane1 %>%
  group_by(color = `Group Color`) %>%
  summarize(s = sum(Quantity)) %>%
  filter(color != "Unknown") %>%
  arrange(desc(s))

dane1$`Group Color` = factor(dane1$`Group Color`, levels = kolor$color)

dane1 %>%
  group_by(`Group Color`) %>%
  arrange(desc(desc(dane1$`Group Color`))) %>%
  ggplot(mapping = aes(y = as.factor(`Group Color`), 
                       x=Quantity, 
                       fill=as.factor(`Item Size`))) +
  geom_bar(stat = "identity",position="stack") +
  theme_bw() +
  scale_fill_brewer(palette="Set3") +
  scale_color_brewer(palette = "Set3") +
  labs(title = "Ilość sprzedanych produktów wg koloru i rozmiaru",
       x = "sprzedana ilość" ,
       y = "kolor" ,
       fill = "rozmiar")

Wnioski

Powyższy wykres w bardzo ciekawy sposób prezentuje nam strukturę produktów. Możemy zobaczyć najpopularniejsze kolory w ofercie firmy (Black, White, Navy itd.), oraz podział sprzedaży kolorów w zależności na rozmiar. Wyciągnąć można tutaj 3 wnioski, które należałoby poddać dalszej analizie.

1.Rozkład w kolorze „Natural”, jako jedyny diametralnie różni się od pozostałych kolorów. Praktycznie cała sprzedaż odbywa się na jednym rozmiarze – „0”, co w przeciwieństwie do innych kolorów jest bardzo nietypowe.

  1. Rozmiary 4, 5 i 6 maja bardzo podobny udział w sprzedaży wszystkich kolorów (kolor „Natural” jest tutaj wyjątkiem, patrz pkt. 1). Muszą to być najpopularniejsze rozmiary, bez względu na rodzaj produktu, markę i kolor. Bardzo mocno górują one nad pozostałymi rozmiarami.

3.Rozmiar „0”, występujący zauważalnie w sprzedaży wszystkich kolorów (prawdopodobnie produkty jednorozmiarowe – patrz wniosek z pkt. 5) jest nadreprezntowany w kolorach: „Black”, Navy i Natural.